Метеостанция на основе ESP8266
Метеостанция построенная на ESP8266. Состоит из ESP8266 с платформой разработки NodeMCU. Механические части станции, анемометр, дождемер и термогигрометр были получены со станции WMR88. Работает анемометр. Поворот крыльчатки активирует магнитный геркон (2,5 км / ч, переключая каждую секунду). Направление получается с помощью 8 магнитных геркона. Дождемер состоит из простого магнитного геркона, каждый 0,8 мм коромысла ковша активирует магнитный геркон. Вот схема подключения различных датчиков, позаимствованна на одном из зарубежных ресурсов. надо сказать что устройство работало около 8 месяцев, после чего перестало передавать данные.
Схема.
Код автора с зарубежного ресурса.
/*
* Personnal Weather Station sketch for Weater Underground with a ESP8266
* BLANCHARD Jordan — http://www.chynehome.com
*/
#include
#include
#include
#include
#include
#include
#include
#include
ESP8266WiFiMulti wifiMulti;
extern «C» {
#include «user_interface.h»
}
//———————Setup Wifi——————
const char* ssid1 = «SSID_1»; //I use multiple SSID In my home, but you can only define one.
const char* password1 = «PASS_1»;
const char* ssid2 = «SSID_2»;
const char* password2 = «PASS_2»;
const char* ssid3 = «SSID_3»;
const char* password3 = «PASS_3»;
const char* ssid4 = «SSID_4»;
const char* password4 = «PASS_4»;
const char* ssid5 = «SSID_5»;
const char* password5 = «PASS_5»;
//———————WU PSW ID——————-
const char* WUID = «WU_PSW_ID»;
const char* WUPASS = «PASSWORD_WU»;
float altitudepws = 125.00; //LOCAL Alitude of the PWS to get relative pressure
//———————NTP VAR———————
unsigned int localPort = 2390;
IPAddress timeServerIP;
const char* ntpServerName = «time.nist.gov»;
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[ NTP_PACKET_SIZE];
WiFiUDP udp;
unsigned long epoch;
//———————WEATHER VAR——————
float windspeed_eu;
float windgust_eu;
float winddir_eu;
float tempout_eu;
float dewpout_eu;
float humidity_eu;
float baro_eu;
float rain1h_eu;
float rain24h_eu;
//US
String windspeed;
String windgust;
String winddir;
String tempout;
String dewpout;
String humidity;
String baro;
String rain1h;
String rain24h;
int radio = 60; // Radio from vertical anemometer axis to a cup center (mm)
int temp_offset = -4; // Temp. Offset
int humi_offset = 10; // Humidity Offset
//———————PIN GPIO MAP—————-
int rainils = 10; //Rain REED-ILS sensor
int windils = 14; //WIND REED-ILS sensor
int windsensor = A0; //Wind Dir sensor
//———————PROG VAR———————
const char* host = «weatherstation.wunderground.com»;
unsigned long count5sec;
unsigned long count60sec;
unsigned long count1h;
unsigned int pulseswind;
unsigned int pulsesgust;
float tempwindgust;
bool debug = 0; //debug = 1 -> enable debug
float raincount = 0.00;
float raincount1h = 0.00;
const float pi = 3.14159265; // pi number
float calcgustspeed;
float calcwindspeed;
float sensor_count = 0.00f;
float winddir_sum = 0.00f;
float tempout_sum = 0.00f;
float humidity_sum = 0.00f;
float baro_sum = 0.00f;
unsigned long rain_last=0;
unsigned long wind_last=0;
int addr=0;
float pressure;
String eepromstring = «0.00»;
Adafruit_BME280 bme;
void setup(){
pinMode(rainils, INPUT);
pinMode(windils, INPUT);
//wifi_set_sleep_type(NONE_SLEEP_T);
//wifi_set_sleep_type(MODEM_SLEEP_T);
wifi_set_sleep_type(LIGHT_SLEEP_T);
Serial.begin(115200);
Serial.print(«Start Weather Station «);
Serial.println(WUID);
attachInterrupt(windils, rpm, FALLING);
attachInterrupt(rainils, rain, FALLING);
startwifi();
Wire.begin();
if (!bme.begin()) {
Serial.println(«Could not find a valid BME280 sensor, check wiring!»);
ESP.restart();
}
EEPROM.begin(512);
//RESET EEPROM CONTENT — ONLY EXECUTE ONE TIME — AFTER COMMENT
/*Serial.println(«CLEAR «);
eepromClear();
Serial.println(«SET «);
eepromSet(«raincount», «9.00»);
eepromSet(«raincount1h», «0.00»);
Serial.println(«LIST «);
Serial.println(eepromList());*/
//END — RESET EEPROM CONTENT — ONLY EXECUTE ONE TIME — AFTER COMMENT
//GET STORED RAINCOUNT IN EEPROM
Serial.println(«GET EEPROM»);
eepromstring=eepromGet(«raincount»);
raincount=eepromstring.toFloat();
Serial.print(«RAINCOUNT VALUE FROM EEPROM: «);
Serial.println(raincount);
eepromstring=eepromGet(«raincount1h»);
raincount1h=eepromstring.toFloat();
Serial.print(«RAINCOUNT1H VALUE FROM EEPROM: «);
Serial.println(raincount1h);
//END — GET STORED RAINCOUNT IN EEPROM
if (raincount1h==0)
{
count1h = millis();
}
count5sec = millis();
count60sec = millis();
//start interupt
sei();
pulseswind = 0;
pulsesgust = 0;
}
void loop(){
if ( (millis() — count5sec) >= 5000)
{
//Call speedgust() to store actual wind gust
Serial.print(«Take gust values each 5sec: «);
Serial.println(speedgust());
//Add actual wind direction to average after 60sec
winddir_sum = winddir_sum + DirWind();
tempout_sum = tempout_sum + ( bme.readTemperature() + temp_offset );
humidity_sum = humidity_sum + ( bme.readHumidity() + humi_offset );
baro_sum = baro_sum + ( bme.readSealevelPressure(altitudepws)/100.00f );
sensor_count = sensor_count + 1.00f;
count5sec = millis();
if (debug) {
Serial.print(«Other Sensor each 5sec: «);
tempout_eu = tempout_sum / sensor_count;
humidity_eu = humidity_sum / sensor_count;
dewpout_eu = ( tempout_eu — ((100.00f — humidity_eu)/5.00f) );
baro_eu = baro_sum / sensor_count;
winddir_eu = winddir_sum / sensor_count;
Serial.print(«Temp: «);
Serial.print(tempout_eu);
Serial.print(» — Dew Point: «);
Serial.print(dewpout_eu);
Serial.print(» — Humidity: «);
Serial.print(humidity_eu);
Serial.print(» — Pressure: «);
Serial.print(baro_eu);
Serial.print(» — Wind Dir: «);
Serial.println(winddir_eu);
}
}
if ( (millis() — count60sec) >= 60000)
{
ntptime();
Serial.println(«»);
Serial.println(«Actual Local Time:»);
Serial.print(«Hour: «);
Serial.println(localhour());
Serial.print(«Min: «);
Serial.println(localmin());
Serial.print(«Sec: «);
Serial.println(localsec());
Serial.println(«»);
Serial.println(«Store and convert all sensor value fo WU each 60sec»);
//reset Daily Rain each 24h
if ((localhour() >= 23) && (localmin() >= 55))
{
Serial.println(«Reset Daily Rain»);
raincount = 0;
rain24h_eu = 0.00;
}
//get all value of sensor
winddir_eu = winddir_sum / sensor_count;
windspeed_eu = speedwind();
//wind gust for 60sec
windgust_eu = tempwindgust;
tempwindgust = 0;
tempout_eu = tempout_sum / sensor_count;
humidity_eu = humidity_sum / sensor_count;
dewpout_eu = ( tempout_eu — ((100.00f — humidity_eu)/5.00f) );
baro_eu = baro_sum / sensor_count;
rain1h_eu = 0.80f * raincount1h;
rain24h_eu = 0.800f * raincount;
winddir_sum = 0.00f;
tempout_sum = 0.00f;
humidity_sum = 0.00f;
baro_sum = 0.00f;
sensor_count = 0.00f;
Serial.println(» «);
Serial.println(«EU to US conversion for WU «);
Serial.println(» «);
Serial.println(«EU: «);
Serial.print(«Temp: «);
Serial.println(tempout_eu);
Serial.print(«Dew Point: «);
Serial.println(dewpout_eu);
Serial.print(«Humidity: «);
Serial.println(humidity_eu);
Serial.print(«Pressure: «);
Serial.println(baro_eu);
Serial.print(«Wind Speed: «);
Serial.println(windspeed_eu);
Serial.print(«Wind Gust: «);
Serial.println(windgust_eu);
Serial.print(«Wind Direction: «);
Serial.println(winddir_eu);
Serial.print(«Rain 1h: «);
Serial.println(rain1h_eu);
Serial.print(«Rain 24h: «);
Serial.println(rain24h_eu);
//make conversion to US for Wunderground
windspeed = windspeed_eu * 0.62138f;
windgust = windgust_eu * 0.62138f;
winddir = winddir_eu;
tempout = (( tempout_eu * 1.8 ) + 32);
dewpout = (( dewpout_eu * 1.8 ) + 32);
humidity = humidity_eu;
baro = 0.02952998751 * baro_eu;
rain1h = rain1h_eu / 25.40 ;
rain24h = rain24h_eu / 25.40 ;
Serial.println(» «);
Serial.println(«US: «);
Serial.print(«Temp: «);
Serial.println(tempout);
Serial.print(«Dew Point: «);
Serial.println(dewpout);
Serial.print(«Humidity: «);
Serial.println(humidity);
Serial.print(«Pressure: «);
Serial.println(baro);
Serial.print(«Wind Speed: «);
Serial.println(windspeed);
Serial.print(«Wind Gust: «);
Serial.println(windgust);
Serial.print(«Wind Direction: «);
Serial.println(winddir);
Serial.print(«Rain 1h: «);
Serial.println(rain1h);
Serial.print(«Rain 24h: «);
Serial.println(rain24h);
Serial.println(» «);
Serial.println(«Send Data to WU each 60sec»);
//STORE RAINCOUNT IN EEPROM
Serial.println(«SET EEPROM»);
eepromstring = String(raincount,2);
eepromSet(«raincount», eepromstring);
eepromstring = String(raincount1h,2);
eepromSet(«raincount1h», eepromstring);
//END — STORE RAINCOUNT IN EEPROM
senddata();
count60sec = millis();
//ESP.restart();
}
if ( ((millis() — count1h) >= (60000*60*1)) && (raincount1h != 0))
{
Serial.println(«Reset hourly rain each hour»);
raincount1h = 0;
rain1h_eu = 0.00;
}
if ( millis() >= (60000*60*24*3))
{
Serial.println(«task each week»);
ESP.restart();
}
}
//———————————————————WIFI SETUP———————————————-
void startwifi()
{
Serial.print(«Connecting to Wifi»);
wifiMulti.addAP(ssid1, password1); //if you have less SSID, delete the others
wifiMulti.addAP(ssid2, password2);
wifiMulti.addAP(ssid3, password3);
wifiMulti.addAP(ssid4, password4);
wifiMulti.addAP(ssid5, password5);
while (wifiMulti.run() != WL_CONNECTED) {
delay(500);
Serial.print(«.»);
}
Serial.println(«»);
Serial.println(«WiFi connected»);
Serial.println(«IP address: «);
Serial.println(WiFi.localIP());
startudp();
}
//———————————————————SEND TO WU———————————————-
void senddata()
{
Serial.println(«Send to WU Sensor Values»);
Serial.print(«connecting to «);
Serial.println(host);
// Use WiFiClient class to create TCP connections
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println(«connection failed»);
startwifi();
return;
}
// We now create a URI for the request
String url = «/weatherstation/updateweatherstation.php?ID=»;
url += WUID;
url += «&PASSWORD=»;
url += WUPASS;
url += «&dateutc=now&winddir=»;
url += winddir;
url += «&windspeedmph=»;
url += windspeed;
url += «&windgustmph=»;
url += windgust;
url += «&tempf=»;
url += tempout;
url += «&dewptf=»;
url += dewpout;
url += «&humidity=»;
url += humidity;
url += «&baromin=»;
url += baro;
url += «&rainin=»;
url += rain1h;
url += «&dailyrainin=»;
url += rain24h;
url += «&weather=&clouds=&softwaretype=Arduino-ESP8266&action=updateraw»;
Serial.print(«Requesting URL: «);
Serial.println(url);
// This will send the request to the server
client.print(String(«GET «) + url + » HTTP/1.1\r\n» +
«Host: » + host + «\r\n» +
«Connection: close\r\n\r\n»);
delay(10);
// Read all the lines of the reply from server and print them to Serial
while(client.available()){
String line = client.readStringUntil(‘\r’);
Serial.print(line);
}
Serial.println();
Serial.println(«closing connection»);
//wifi_set_sleep_type(NONE_SLEEP_T);
//wifi_set_sleep_type(MODEM_SLEEP_T);
wifi_set_sleep_type(LIGHT_SLEEP_T);
}
//——————————————————Wind Direction—————————————————————
float DirWind(){
int winddirstate = analogRead(windsensor);
if ( (winddirstate >= 210) && (winddirstate <= 220) ) { winddir_eu = 0; } if ( (winddirstate >= 0) && (winddirstate <= 25) ) { winddir_eu = 45; } if ( (winddirstate >= 25) && (winddirstate <= 75) ) { winddir_eu = 90; } if ( (winddirstate >= 75) && (winddirstate <= 120) ) { winddir_eu = 135; } if ( (winddirstate >= 120) && (winddirstate <= 150) ) { winddir_eu = 180; } if ( (winddirstate >= 150) && (winddirstate <= 180) ) { winddir_eu = 225; } if ( (winddirstate >= 180) && (winddirstate <= 205) ) { winddir_eu = 270; } if ( (winddirstate >= 205) && (winddirstate <= 210) ) { winddir_eu = 315; } if (debug) { Serial.print(«Wind Dir: «); Serial.print(winddir_eu); Serial.print(» Pin Status: «); Serial.println(winddirstate); } return winddir_eu; } //——————————————————-Interupt Wind and Rain——————————————————— void rpm() { long thisTime=micros()-wind_last; wind_last=micros(); if(thisTime>500)
{
pulseswind++;
pulsesgust++;
if (debug) {
Serial.print(«Nb wind turn: «);
Serial.println(pulseswind);
}
}
}
// Interrupt routine
void rain() {
long thisTime=micros()-rain_last;
rain_last=micros();
if(thisTime>1000)
{
if (raincount1h==0)
{
count1h = millis();
}
raincount1h = raincount1h + 1.00f;
raincount = raincount + 1.00f;
if (debug) {
Serial.print(«Nb rain drop: «);
Serial.println(raincount);
}
}
}
//———————————————————Wind Speed———————————————-
float speedwind()
{
// cli();
float pulseswindrmp = ( pulseswind / 60.00f );
calcwindspeed = ( pulseswindrmp * 2.50f );
if (calcwindspeed > tempwindgust)
{
tempwindgust = calcwindspeed;
}
if (debug) {
Serial.print(«Total pulseswindrmp: «);
Serial.print(pulseswindrmp);
Serial.print(» Wind Speed: «);
Serial.println(calcwindspeed);
}
pulseswind = 0;
//sei();
return calcwindspeed;
}
//———————————————————Wind Gust———————————————-
float speedgust()
{
//cli();
float pulsesgustrmp = ( pulsesgust / 5.00f );
calcgustspeed = ( pulsesgustrmp * 2.50f );
if (calcgustspeed > tempwindgust)
{
tempwindgust = calcgustspeed;
}
if (debug) {
Serial.print(«Total pulsesgustrmp: «);
Serial.print(pulsesgustrmp);
Serial.print(» Gust Speed: «);
Serial.println(calcgustspeed);
}
pulsesgust = 0;
//sei();
return calcgustspeed;
}
//———————————————————UDP NTP—————————————————
void startudp()
{
Serial.println(«Starting UDP»);
udp.begin(localPort);
Serial.print(«Local port: «);
Serial.println(udp.localPort());
}
//———————————————————NTP request———————————————-
unsigned long sendNTPpacket(IPAddress& address)
{
Serial.println(«sending NTP packet…»);
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
udp.beginPacket(address, 123); //NTP requests are to port 123
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
}
//———————————————————NTP Time———————————————-
unsigned long ntptime()
{
WiFi.hostByName(ntpServerName, timeServerIP);
sendNTPpacket(timeServerIP);
delay(1000);
int cb = udp.parsePacket();
if (!cb) {
Serial.println(«no NTP packet yet»);
}
else {
Serial.print(«NTP packet received, length=»);
Serial.println(cb);
// We’ve received a packet, read the data from it
udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
// now convert NTP time into everyday time:
// Unix time starts on Jan 1 1970. In seconds, that’s 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
epoch = secsSince1900 — seventyYears;
if (debug) {
Serial.print(«Seconds since Jan 1 1900 = » );
Serial.println(secsSince1900);
// print Unix time:
Serial.print(«Unix time = «);
Serial.println(epoch);
// print the hour, minute and second:
Serial.print(«The local time (UTC-4) is «); // UTC-4 by (epoch-(3600*4))
Serial.print(((epoch-(3600*4)) % 86400L) / 3600); // print the hour (86400 equals secs per day)
Serial.print(‘:’);
if ( (((epoch-(3600*4)) % 3600) / 60) < 10 ) {
// In the first 10 minutes of each hour, we’ll want a leading ‘0’
Serial.print(‘0’);
}
Serial.print(((epoch-(3600*4)) % 3600) / 60); // print the minute (3600 equals secs per hour)
Serial.print(‘:’);
if ( ((epoch-(3600*4)) % 60) < 10 ) {
// In the first 10 seconds of each minute, we’ll want a leading ‘0’
Serial.print(‘0’);
}
Serial.println((epoch-(3600*4)) % 60); // print the second
}
}
}
int localhour()
{
return (((epoch-(3600*4)) % 86400L) / 3600);
}
int localmin()
{
return (((epoch-(3600*4)) % 3600) / 60);
}
int localsec()
{
return ((epoch-(3600*4)) % 60);
}
//———————————————————EEPROM———————————————-
void eepromSet(String name, String value){
Serial.println(«eepromSet»);
String list=eepromDelete(name);
String nameValue=»&» + name + «=» + value;
//Serial.println(list);
//Serial.println(nameValue);
list+=nameValue;
for (int i = 0; i < list.length(); ++i){
EEPROM.write(i,list.charAt(i));
}
EEPROM.commit();
Serial.print(name);
Serial.print(«:»);
Serial.println(value);
}
String eepromDelete(String name){
Serial.println(«eepromDelete»);
int nameOfValue;
String currentName=»»;
String currentValue=»»;
int foundIt=0;
char letter;
String newList=»»;
for (int i = 0; i < 512; ++i){ letter= char(EEPROM.read(i)); if (letter==’\n’){ if (foundIt==1){ }else if (newList.length()>0){
newList+=»=» + currentValue;
}
break;
} else if (letter==’&’){
nameOfValue=0;
currentName=»»;
if (foundIt==1){
foundIt=0;
}else if (newList.length()>0){
newList+=»=» + currentValue;
}
} else if (letter==’=’){
if (currentName==name){
foundIt=1;
}else{
foundIt=0;
newList+=»&» + currentName;
}
nameOfValue=1;
currentValue=»»;
}
else{
if (nameOfValue==0){
currentName+=letter;
}else{
currentValue+=letter;
}
}
}
for (int i = 0; i < 512; ++i){
EEPROM.write(i,’\n’);
}
EEPROM.commit();
for (int i = 0; i < newList.length(); ++i){
EEPROM.write(i,newList.charAt(i));
}
EEPROM.commit();
Serial.println(name);
Serial.println(newList);
return newList;
}
void eepromClear(){
Serial.println(«eepromClear»);
for (int i = 0; i < 512; ++i){
EEPROM.write(i,’\n’);
}
}
String eepromList(){
Serial.println(«eepromList»);
char letter;
String list=»»;
for (int i = 0; i < 512; ++i){
letter= char(EEPROM.read(i));
if (letter==’\n’){
break;
}else{
list+=letter;
}
}
Serial.println(list);
return list;
}
String eepromGet(String name){
Serial.println(«eepromGet»);
int nameOfValue;
String currentName=»»;
String currentValue=»»;
int foundIt=0;
String value=»»;
char letter;
for (int i = 0; i < 512; ++i){
letter= char(EEPROM.read(i));
if (letter==’\n’){
if (foundIt==1){
value=currentValue;
}
break;
} else if (letter==’&’){
nameOfValue=0;
currentName=»»;
if (foundIt==1){
value=currentValue;
break;
}
} else if (letter==’=’){
if (currentName==name){
foundIt=1;
}else{
}
nameOfValue=1;
currentValue=»»;
}
else{
if (nameOfValue==0){
currentName+=letter;
}else{
currentValue+=letter;
}
}
}
Serial.print(name);
Serial.print(«:»);
Serial.println(value);
return value;
}